#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <signal.h>
#include <arpa/inet.h>

#include "tkmain.h"
#include "daemontools.h"
#include "message.dsc.h"
#include "memberlist.dsc.h"
#include "tklog.h"
#include "tklist.h"
#include "tkhandler.h"
#include "tkproto.h"
#include "tkerror.h"

struct serverenv gEnv; 
struct config gConf; 

void add_client_conn(struct tk_connect *conn)
{
	list_add_tail(&conn->list_node, gEnv.pclients_list);
}

void del_client_conn(struct tk_connect *conn)
{
	list_del_init(&conn->list_node);
}

void do_broadcast_message(struct tk_connect *conn, struct tk_message *msg)
{
	struct tk_connect *pos = NULL, *n = NULL;
	struct custom_socket *s = NULL;
	struct member *mem = NULL;
	int r;
	
        list_for_each_entry_safe(pos, n, gEnv.pclients_list, list_node) {

		if (pos == conn || pos->failed == 1 || pos->finished == 1)
			continue;

		s = pos->csock;
		mem = (struct member*)pos->client_ptr;

		r = tk_do_add_send_msg(pos, msg);
		if (r == TK_NEED_ADD_W_EVENT) {
			r = epoll_add_write_event(gEnv.epoll_env, s->event);
			if (r) {
				pos->failed = 1;
				elog("client[%s]-ip[%s] force closed", mem->clientname, s->addr.ipv4);
			}
		}
		else if (r && r != TK_NEED_ADD_W_EVENT) {
			elog("client[%s]-ip[%s] tk_do_add_send_msg failed", mem->clientname, s->addr.ipv4);
		}
        }	
}

int do_send_init_member_list(struct tk_connect *conn)
{
	struct custom_socket *s = NULL;
	struct tk_message msg;
	int r;
	
	s = conn->csock;		
	
	memset(&msg, 0x00, sizeof(struct tk_message));
	msg.body_len = MAXMESSAGELEN;
	r = DSCSERIALIZE_JSON_memberlist(&gEnv.member_list, "GBK", msg.body, &msg.body_len);
	if (r) {
		elog("DSCSERIALIZE_JSON_memberlist failed");	
		return r;
	}
	
	r = tk_do_add_send_msg(conn, &msg);
	if (r) {
		elog("tk_do_add_send_msg failed");
		return r;
	}

	return TK_OK;
}

/* when connection occurs error */
int do_connect_error_handler(struct tk_connect *conn)
{
	struct custom_socket *s = NULL;

	/*TODO */
	/* clean memory */
	
	/* update clients information */

	/* syn information to other clients */

	return TK_OK;
}

struct member* main_insert_member(struct member *mem)
{
	int r;
	r = add_member(&gEnv.member_list, mem);
	if (r) {
		return NULL;
	}
	else {
		return get_last_member(&gEnv.member_list);
	}
}

int main_delete_member(struct member *mem)
{
	return del_member_byname(&gEnv.member_list, mem);
}

void main_insert_connect(struct list_head *new)
{
	list_add_tail(new, gEnv.pclients_list);
}

int main_process_clients_list()
{
	int r;
        struct tk_connect *conn = NULL, *n = NULL;
        struct custom_socket *s = NULL;
        memberlist del_member_list;
	memberlist add_member_list;
	int shoud_sync_member_list = 0;
	memberlist sync_member_list;
	struct member *tmp_member = NULL;
	struct tk_message tk_msg;
	int len;

	init_member_list(&del_member_list, MEMBER_CMD_DELETE); 
	init_member_list(&add_member_list, MEMBER_CMD_ADD); 

	/* search for del clients */
        list_for_each_entry_safe(conn, n, gEnv.pclients_list, list_node) {

		tmp_member = (struct member*)conn->client_ptr;

                if (conn->failed == 1) {
			if (	conn->stage == TK_CONNECT_SYNC_CLIENT_INFO 
				|| 
				conn->stage == TK_CONNECT_SYNC_CLIENT_INFO_DONE 
				|| 
				conn->stage == TK_CONNECT_SYNC_CLIENTS_LIST 
			) {
				tk_connection_error(gEnv.epoll_env, conn);
			}
			else if (conn->stage == TK_CONNECT_SYNC_CLIENTS_LIST_DONE || conn->stage == TK_CONNECT_COMMUNICATING) {

				add_member(&del_member_list, tmp_member);
				tk_connection_error(gEnv.epoll_env, conn);
				shoud_sync_member_list = 1;
			}
		}
		else if (conn->finished = 1) {
                        if (    conn->stage == TK_CONNECT_SYNC_CLIENT_INFO
                                ||
                                conn->stage == TK_CONNECT_SYNC_CLIENT_INFO_DONE
                                ||
                                conn->stage == TK_CONNECT_SYNC_CLIENTS_LIST
                        ) {
                                tk_connection_error(gEnv.epoll_env, conn);
                        }
                        else if (conn->stage == TK_CONNECT_SYNC_CLIENTS_LIST_DONE || conn->stage == TK_CONNECT_COMMUNICATING) {

                                add_member(&del_member_list, tmp_member);
                                tk_connection_error(gEnv.epoll_env, conn);
				shoud_sync_member_list = 1;
                        }
		}
        }

	/* search for add clients */	
	list_for_each_entry_safe(conn, n, gEnv.pclients_list, list_node) {

                tmp_member = (struct member*)conn->client_ptr;

                if (conn->failed == 1 || conn->finished == 1) {
			continue;
		}

		if (    conn->stage == TK_CONNECT_SYNC_CLIENT_INFO
			||
			conn->stage == TK_CONNECT_SYNC_CLIENT_INFO_DONE
			||
			conn->stage == TK_CONNECT_SYNC_CLIENTS_LIST
		) {
			continue;
		}
		else if (conn->stage == TK_CONNECT_SYNC_CLIENTS_LIST_DONE) {
			tk_start_communicate(conn);
			add_member(&add_member_list, tmp_member);
			shoud_sync_member_list = 1;
		}
        }

	if (!shoud_sync_member_list) {
		return 0;
	}

	/* sync member list */
	list_for_each_entry_safe(conn, n, gEnv.pclients_list, list_node) {

                tmp_member = (struct member*)conn->client_ptr;

                if (conn->failed == 1 || conn->finished == 1) {
			continue;
		}

		if (conn->stage == TK_CONNECT_COMMUNICATING) {
			add_member(&sync_member_list, tmp_member);
		}
	}

	/* add sync client list message for live clients */
	r = build_tk_message_with_memberlist(&tk_msg, &sync_member_list);
	if (r) {
		elog("DSCSERIALIZE_JSON_memberlist sync list failed, exit!");
		//TODO main_error();
	}

	list_for_each_entry_safe(conn, n, gEnv.pclients_list, list_node) {
                tmp_member = (struct member*)conn->client_ptr;
		r = tk_do_add_send_msg(conn, &tk_msg);
		if (r) {
			elog("clientname[%s] - ip[%s] not enough msg space", tmp_member->clientname, tmp_member->ip);
		}
	}

	return 0;
}

void main_error()
{
	destory_epoll_env(gEnv.epoll_env);
	exit(0);
}

int main()
{
	int r;
	FILE *logfile = NULL;
	char logpathfile[FILEPATHMAXLEN];
	struct sockaddr_in sock;
	memset(&gEnv, 0x00, sizeof(gEnv));
	memset(&gConf, 0x00, sizeof(gConf));
	setvbuf(stdout, NULL, _IONBF, 0);

	gEnv.conf = &gConf;
        gEnv.pclients_list = (struct list_head*)(malloc(sizeof(struct list_head)));
        if (gEnv.pclients_list == NULL) {
		printf("initialize client list error\n");		
                return TK_ERROR;
        }
        INIT_LIST_HEAD(gEnv.pclients_list);
	
	init_member_list(&gEnv.member_list, MEMBER_CMD_INIT);	

	/* initialize config */
	strcpy(gConf.logpath, "./");
	gConf.serverport = 9000;
	strcpy(gConf.tkhistory, "his.dat");
	gConf.maxconnecting = 50;
	
	/* initialize log */
	memset(logpathfile, 0x00, sizeof(logpathfile));	
	memcpy(logpathfile, gConf.logpath, sizeof(gConf.logpath));
	strcat(logpathfile, "minitalk.log");
	if ((logfile = fopen(logpathfile, "a+")) == NULL) {
		printf("initialize log file failed\n");
		return 0;	
	}
	setlogfile(logfile);
	setloglevel(DEBUG);
	

	/* initialize epoll */
	if ((gEnv.epoll_env = create_epoll_env(do_after_accept_entry, NULL)) == NULL) {
		elog("create epoll env failed");
		return 0;
	}

	/* init listen socket */
	gEnv.listenfd = socket(PF_INET, SOCK_STREAM, 0);
	memset(&sock, 0x00, sizeof(struct sockaddr_in));
	sock.sin_port = htons(gEnv.serverport);
	sock.sin_family = PF_INET;
	sock.sin_addr.s_addr = htonl(INADDR_ANY);
	// inet_pton(addr.sin_family, li->listen_address.ip, &addr.sin_addr.s_addr)
	r = bind(gEnv.listenfd, &sock, sizeof(struct sockaddr_in));
	if (r) {
		elog("bind listen socket error");
		return 0;
	}

	sb_set_nonblocking(gEnv->listenfd)
	sb_set_reuseaddr(li->listen_fd, 1);

	r = listen(gEnv.listenfd, 1024);
	if (r) {
		elog("listen error"); 
		return 0;
	}	

	/* start daemon */
	printf("Start minitalk daemon init...\n");
	ilog("Start minitalk daemon init...");
		
/*
	if (todaemon() != 0) {
		elog("initailize daemon failed!");
		exit(-1);
	}
*/

	/* enter main event loop */
        while (1) {
                r = epoll_process_events(gEnv.epoll_env, 100);
                if (r < 0) {
                        elog("epoll_process_events failed");
                        exit(1);
                }
        }

ERR:
	main_error();

	return 0;
}
